﻿using DataSync.Entity;
using log4net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Boco.CommonLib;
using DataSync.Common;
using System.Xml.Linq;

namespace DataSync.Proc.Reader
{
    abstract class FileDataReader : DataSyncReaderBase
    {
        private static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private static object _lock = new object();
        protected FileReaderConfig CurReaderConf; //文件读取配置
        protected FileParseConfig CurParseConf; //文件解析配置

        private readonly string CNT_DicFileName = "FileName";
        private CustParseExtData extCustParseData;
        Dictionary<string, ICustParse> custParseIns; //转换器列表
        private XElement CurConfNode;
        /// <summary>
        /// 读取数据源
        /// </summary>
        /// <param name="xmlnode"></param>
        protected abstract void ReadSourceConfig(XElement xmlnode);

        /// <summary>
        /// 读取配置
        /// </summary>
        /// <param name="xmlnode"></param>
        protected override void ReadConfig(XElement xmlnode)
        {
            CurConfNode = xmlnode;
            //读取配置
            var fconf = xmlnode.Element("FileConfig");
            if (fconf == null)
            {
                throw new ApplicationException("任务FileConfig配置不正确");
            }
            CurReaderConf = new FileReaderConfig()
            {
                LocalDir = fconf.GetAttribute("LocalDir"),
                BackDir = fconf.GetAttribute("BackDir"),
                FileName = fconf.GetAttribute("FileName"),
                Type = "File",
                DeleteSourceFile = fconf.GetAttribute("DeleteSourceFile").ToUpper() == "TRUE" ? true : false
            };

            if (string.IsNullOrEmpty(CurReaderConf.LocalDir))
            {
                CurReaderConf.LocalDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DataFile");
            }
            if (!Directory.Exists(CurReaderConf.LocalDir))
            {
                Directory.CreateDirectory(CurReaderConf.LocalDir);
            }
            if (!Directory.Exists(CurReaderConf.BackDir) && !string.IsNullOrEmpty(CurReaderConf.BackDir))
            {
                Directory.CreateDirectory(CurReaderConf.BackDir);
            }

            //读取 具体源配置
            var sourcenode = xmlnode.Element("FtpConfig").Element("Source");
            if (sourcenode == null)
            {
                throw new ApplicationException("FileParse配置不正确,未找到Source配置项");
            }
            ReadSourceConfig(sourcenode);

            //读取解析配置
            var parseconf = xmlnode.Element("FileParse");
            if (parseconf == null)
            {
                throw new ApplicationException("FileParse配置不正确");
            }
            FileParseConfig pconf = new FileParseConfig()
            {
                OffsetRow = parseconf.GetAttributesAsInt("OffsetRow"),
                TitleRow = parseconf.GetAttributesAsInt("TitleRow"),
                ColSplit = parseconf.GetAttribute("ColSplit"),
                FileType = parseconf.GetAttribute("FileType"),
                BathDataCount = parseconf.GetAttributesAsInt("BathDataCount"),
                RowSplit = parseconf.GetAttribute("RowSplit")
            };
            if (pconf.BathDataCount <= 0)
            {
                pconf.BathDataCount = 1000;
            }
            if (string.IsNullOrEmpty(pconf.ColSplit))
            {
                pconf.ColSplit = ",";
            }
            string encodestr = parseconf.GetAttribute("Encoding");
            if (string.IsNullOrEmpty(encodestr))
            {
                pconf.Encoding = Encoding.Default;
            }
            else
            {
                pconf.Encoding = Encoding.GetEncoding(encodestr);
            }
            //读取解析字段
            var fnode = parseconf.Element("Fields").Elements("Field");
            pconf.Fields = new List<ParseField>();
            pconf.DataEnums = new Dictionary<string, List<DataEnumItem>>();
            foreach (var item in fnode)
            {
                pconf.Fields.Add(new ParseField()
                {
                    Index = item.GetAttributesAsInt("Index"),
                    Name = item.GetAttribute("Name"),
                    Col = item.GetAttribute("Col"),
                    FieldType = item.GetAttribute("FieldType"),
                    ConvertType = item.GetAttribute("ConvertType"),
                    ConvertParams = item.GetAttribute("ConvertParams"),
                    Iskey = item.GetAttributesAsBool("Iskey")
                });
            }
            //for (int i = 0; i < fnode.Count; i++)
            //{
            //    pconf.Fields.Add(new ParseField()
            //    {
            //        Index = fnode[i].GetAttributesAsInt("Index"),
            //        Name = fnode[i].GetAttribute("Name"),
            //        Col = fnode[i].GetAttribute("Col"),
            //        FieldType = fnode[i].GetAttribute("FieldType"),
            //        ConvertType = fnode[i].GetAttribute("ConvertType"),
            //        ConvertParams = fnode[i].GetAttribute("ConvertParams"),
            //        Iskey = fnode[i].GetAttributesAsBool("Iskey")
            //    });
            //}
            //过滤
            if (parseconf.Element("ExcludeFilters") != null)
            {
                var exnode = parseconf.Element("ExcludeFilters").Elements("Filter");
                foreach (var item in exnode)
                {
                    pconf.ExcludeFilters.Add(new FilterField()
                    {
                        Index = item.GetAttributesAsInt("Index"),
                        Col = item.GetAttribute("Col"),
                        Value = item.GetAttribute("Value")
                    });
                }
            }
            //读取字典解析
            var enode = parseconf.Element("DataEnums");
            if (enode != null)
            {
                var enodes = enode.Elements("DataEnum");
                foreach (var eitem in enodes)
                {
                    List<DataEnumItem> items = new List<DataEnumItem>();
                    var itemNodes = eitem.Elements("Item");
                    foreach (var nitem in itemNodes)
                    {
                        items.Add(new DataEnumItem()
                        {
                            Key = nitem.GetAttribute("Key"),
                            Value = nitem.GetAttribute("Value")
                        });

                    }
                    var name = eitem.GetAttribute("Name");
                    if (pconf.DataEnums.ContainsKey(name))
                    {
                        pconf.DataEnums[name] = items;
                    }
                    else
                        pconf.DataEnums.Add(name, items);
                }
            }

            this.CurParseConf = pconf;
            extCustParseData = new CustParseExtData();
            extCustParseData.DataEnums = this.CurParseConf.DataEnums;
            extCustParseData.DicData = new Dictionary<string, string>();
            //初始化 转换器
            custParseIns = new Dictionary<string, ICustParse>();
            foreach (var item in pconf.Fields)
            {
                if (!string.IsNullOrEmpty(item.ConvertType) && !custParseIns.ContainsKey(item.ConvertType))
                {
                    var custParser = InterfaceFactory.GetCustParse(item.ConvertType);
                    custParser.Init(CurConfNode);
                    custParseIns.Add(item.ConvertType, custParser);
                }
            }
        }

        /// <summary>
        /// 获取本次文件
        /// </summary>
        /// <returns></returns>
        protected abstract List<string> GetFileList();

        string GetFileHisLogFileName()
        {
            return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format("FileHis_{0}.log", CurReaderConf.FileName));
        }

        protected string GetFileHisLog()
        {
            string hasDownloadLogFile = GetFileHisLogFileName();

            if (!File.Exists(hasDownloadLogFile))
            {
                //更新已处理列表
                string[] hasDownFiles = Directory.GetFiles(CurReaderConf.LocalDir);
                List<string> hasDownFileList = new List<string>();
                foreach (var item in hasDownFiles)
                {
                    FileInfo fi = new FileInfo(item);
                    hasDownFileList.Add(fi.Name);
                }

                lock (_lock)
                {
                    File.AppendAllLines(hasDownloadLogFile, hasDownFileList);
                }
            }

            return hasDownloadLogFile;
        }

        protected override int ReadData()
        {
            log.DebugFormat("获取待采集文件列表...");
            //文件列表
            List<string> listFile = GetFileList();
            log.InfoFormat("本次成功获取文件{0}个", listFile.Count);

            log.DebugFormat("采集文件处理...");
            int succNum = 0;
            succNum = FileProc(listFile);

            log.InfoFormat("本次成功处理文件{0}个", succNum);
            return succNum;
        }

        void AddFileHis(string fname)
        {
            string hisLogFile = GetFileHisLogFileName();
            lock (_lock)
            {
                File.AppendAllLines(hisLogFile, new string[] { fname });
            }
        }

        /// <summary>
        /// 文件解析入库
        /// </summary>
        /// <param name="files"></param>
        /// <returns></returns>
        int FileProc(List<string> files)
        {
            int procFileCount = 0;

            foreach (var fname in files)
            {
                string file = Path.Combine(this.CurReaderConf.LocalDir, fname); //文件路径
                log.DebugFormat("处理文件:{0}", fname);

                try
                {
                    //文件解析入库
                    this.ParseProcess(file);

                    //记录文件成功处理
                    AddFileHis(fname);
                    //文件删除
                    if (CurReaderConf.DeleteSourceFile)
                    {
                        //文件删除
                        log.DebugFormat("删除原始文件:{0}", fname);
                        DeleteSourceFile(fname);
                    }
                    //文件备份
                    if (!string.IsNullOrEmpty(CurReaderConf.BackDir))
                    {
                        //文件备份
                        BackFile(fname, file);
                    }

                    procFileCount++;
                }
                catch (Exception ex)
                {
                    log.Error(string.Format("处理文件{0}失败", fname), ex);
                }
                //文件处理完成
                log.InfoFormat("处理文件{0}完成", fname);
            }
            return procFileCount;
        }

        protected abstract void DeleteSourceFile(string file);

        /// <summary>
        /// 文件备份
        /// </summary>
        /// <param name="fname"></param>
        /// <param name="fpath"></param>
        void BackFile(string fname, string fpath)
        {
            try
            {
                var newfpath = Path.Combine(CurReaderConf.BackDir, fname);
                if (File.Exists(newfpath))
                {
                    File.Delete(newfpath);
                }
                File.Move(fpath, newfpath);
            }
            catch (Exception ex)
            {
                log.Warn(string.Format("文件{0}备份失败", fname), ex);
            }
        }

        /// <summary>
        /// 文件解析
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        int ParseProcess(string file)
        {
            int maxColnum = CurParseConf.Fields.Max(p => p.Index);
            //解析数据
            IList<Dictionary<string, ColData>> paseData = new List<Dictionary<string, ColData>>();

            int procCnt = 0;

            //处理文件
            log.InfoFormat("解析文件:{0}", file);
            var fileinfo = new FileInfo(file);
            var fname = fileinfo.Name;

            int rowno = 0;
            procCnt = 0;
            this.CurWriter.PreExecute();

            //bool bRowSplit = !string.IsNullOrEmpty(CurParseConf.RowSplit);
            //char[] RowSplitCharArray = new char[0];
            //if (bRowSplit)
            //{
            //    RowSplitCharArray = CurParseConf.RowSplit.ToCharArray();
            //}

            using (var sr = new StreamReader(file, CurParseConf.Encoding))
            {
                String line;
                paseData.Clear(); //清空
                extCustParseData.DicData.Set(CNT_DicFileName, fname); //设置相关数据

                while ((line = sr.ReadLine()) != null)
                {
                    rowno++;
                    //解析
                    if (string.IsNullOrEmpty(line))
                    {
                        continue;
                    }
                    //if (bRowSplit)  //行处理
                    //{
                    //    while (!line.EndsWith(CurParseConf.RowSplit))
                    //    {
                    //        line += sr.ReadLine();
                    //    }
                    //    line = line.TrimEnd(RowSplitCharArray);
                    //}
                    if (!string.IsNullOrEmpty(CurParseConf.RowSplit))
                    {
                        line = line.TrimEnd(CurParseConf.RowSplit.ToCharArray());
                    }

                    //标题行处理
                    if (rowno == CurParseConf.TitleRow && CurParseConf.TitleRow > 0)
                    {
                        var titleFields = line.Split(new string[] { CurParseConf.ColSplit }, StringSplitOptions.None);

                        //标题字段处理
                        for (int ci = 0; ci < titleFields.Length; ci++)
                        {
                            var col = CurParseConf.Fields.Where(p => p.Col == titleFields[ci]);
                            foreach (var item in col)
                            {
                                item.Index = ci;
                            }
                            var eol = CurParseConf.ExcludeFilters.Where(p => !string.IsNullOrEmpty(p.Col) && p.Col == titleFields[ci]);
                            foreach (var item in eol)
                            {
                                item.Index = ci;
                            }
                        };

                        continue;
                    }

                    if (rowno <= CurParseConf.OffsetRow)
                    {
                        continue;
                    }

                    //分隔
                    var fields = line.Split(new string[] { CurParseConf.ColSplit }, StringSplitOptions.None);

                    if (fields.Length <= maxColnum)
                    {
                        log.WarnFormat("[{0}->line({1})]数据列数不足{2}", fname, rowno, maxColnum);
                        continue;
                    }
                    //过滤检查
                    if (CurParseConf.ExcludeFilters.Count > 0)
                    {
                        bool excludeLineFlag = false;
                        foreach (var exitem in CurParseConf.ExcludeFilters)
                        {
                            if (fields[exitem.Index] == exitem.Value)
                            {
                                //满足排除条件
                                log.WarnFormat("[{0}->line({1})]数据列{2}={3} 排除该行数据", fname, rowno, exitem.Index + 1, exitem.Value);
                                excludeLineFlag = true;
                                continue;
                            }
                        }
                        if (excludeLineFlag) //满足过滤条件
                        {
                            continue;
                        }
                    }
                    //列转换
                    var row = ConvertLine(this.CurParseConf, fields, rowno);
                    paseData.Add(row);

                    if (paseData.Count >= this.CurParseConf.BathDataCount)
                    {
                        log.DebugFormat("入库数据:{0}~{1}", procCnt + 1, procCnt + paseData.Count);
                        //提交数据库
                        CurWriter.Execute(paseData);
                        procCnt += paseData.Count;
                        paseData.Clear();
                    }
                }
            }

            if (paseData.Count > 0)
            {
                log.DebugFormat("入库数据:{0}~{1}", procCnt + 1, procCnt + paseData.Count);
                //提交数据库
                CurWriter.Execute(paseData);
                procCnt += paseData.Count;
                paseData.Clear();
            }

            this.CurWriter.PostExecute();

            log.InfoFormat("解析文件:{0}完成:处理数据条数:{1}", file, procCnt);

            return procCnt;
        }

        protected Dictionary<string, ColData> ConvertLine(LineParseConfig parseConf, string[] linefields, int rowno)
        {
            var row = new Dictionary<string, ColData>();

            foreach (var col in parseConf.Fields)
            {
                var colValue = linefields[col.Index].Replace("'", "").Trim();  //当前列值
                //数据转换
                if (!string.IsNullOrEmpty(col.ConvertType))
                {
                    colValue = this.custParseIns[col.ConvertType].Parse(colValue, linefields, col.ConvertParams, extCustParseData);
                }

                //类型检查与格式化
                string newcolvalue = string.Empty;
                if (DataTypeHelper.CheckType(col.FieldType, colValue, out newcolvalue))
                {
                    colValue = newcolvalue;
                }
                else
                {
                    log.WarnFormat("第{0}-{1}列数据【{2}】不是有效的{3}类型", rowno, col.Index, colValue, col.FieldType);
                    colValue = newcolvalue;
                }

                row.Add(col.Name, new ColData()
                {
                    FieldType = col.FieldType,
                    Value = colValue
                });
            }
            return row;
        }

    }
}
